﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.IO;
using System.Configuration;


namespace Microsoft.Mtool.FSM
{
    [Serializable]
    public class Parameter
    {
        private string name;
        private string description;
        private Variable var = new Variable();

        public Parameter GetCopyWithoutVarValues()
        {
            Parameter p = new Parameter();
            p.Name = name;
            p.Description = description;
            // only copy name, but clear values
            p.Var.Name = var.Name;
            p.Var.MergedValues = null;
            return p;
        }

        [XmlAttribute]
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        [XmlAttribute]
        public string Description
        {
            get { return description; }
            set { description = value; }
        }
        
        [XmlElement]
        public Variable Var
        {
            get { return var; }
            /* ATTENTION: please always use the default instance, this setter is only for serialization */
            set { var = value; } 
        }
    }

    [Serializable]
    abstract public class ActionImpl
    {
        protected string name;
        protected List<Parameter> inParas;
        protected List<Parameter> outParas;

        [XmlAttribute]
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        [XmlArray]
        [XmlArrayItem(Type = typeof(Parameter))]
        public List<Parameter> InParas
        {
            get { return inParas; }
            set { inParas = value; }
        }

        [XmlArray]
        [XmlArrayItem(Type = typeof(Parameter))]
        public List<Parameter> OutParas
        {
            get { return outParas; }
            set { outParas = value; }
        }

        protected string ParasToString(List<Parameter> list)
        {
            if (list == null || list.Count==0) return "";

            string retv = list[0].Var.Name;
            for(int i=1; i<list.Count; i++)
            {
                retv += ", " + list[i].Var.Name;
            }
            return retv;
        }

        [XmlIgnore]
        public string InParasText
        {
            get { return ParasToString(inParas); }
        }

        [XmlIgnore]
        public string OutParasText
        {
            get { return ParasToString(outParas); }
        }

        public string Apply(string inText, string outText, bool isSave)
        {
            // check definition
            ActionImpl defImpl = TokenManager.GetDefinition(name);
            if (defImpl == null) return "Token/Function Not Defined.";
            // check parameters count
            string[] inArray = CheckCount(inText, defImpl.InParas);
            string[] outArray = CheckCount(outText, defImpl.OutParas);
            if (inArray == null) return string.Format("In-Paras Count Error: Required={0}", defImpl.InParas==null ? 0 : defImpl.InParas.Count);
            if (outArray == null) return string.Format("Out-Paras Count Error: Required={0}", defImpl.OutParas == null ? 0 : defImpl.OutParas.Count);
            // check each parameter
            string errorMsg = CheckParas(inArray, outArray);
            if (errorMsg != null) return errorMsg;
            // need not to save
            if (!isSave) return null;
            // no problem, save them
            SaveParas(inArray, inParas);
            SaveParas(outArray, outParas);
            return null;
        }

        private string[] CheckCount(string text, List<Parameter> list)
        {
            string[] parray = {};
            if (list == null || list.Count == 0)
                return string.IsNullOrEmpty(text) ? parray : null;
            else
            {
                if (string.IsNullOrEmpty(text)) return null;
                parray = text.Split(',');
                return (parray.Length == list.Count) ? parray : null;
            }
        }

        private string CheckParas(string[] inArray, string[] outArray)
        {
            //TODO: only can be number or valid variables
            return null;
        }

        private void SaveParas(string[] parray, List<Parameter> list)
        {
            if (list == null || list.Count == 0) return;
            for (int i=0; i<parray.Length; i++)
            {
                list[i].Var.Name = parray[i].Trim();
                list[i].Var.MergedValues = null;
            }
        }

        abstract internal void GenDefaults();
        abstract public ActionImpl GetCopy();
    }
}
